【Clojure入門】 制御構文
Clojureにおける制御構文は「特殊形式」または「マクロ」として提供されています。
Lisp系言語では通常、オペレータ(operator; 演算子)は
関数(function)
マクロ(macro)
の3種類に分類され、 (<オペレータ> <引数1> <引数2> ...) という統一的な構文形式になっています。
「文」(statement)と「式」(expression)の両方を備えた言語も少なくありませんが、Clojureは式のみで構成された徹底した式指向の言語です。
ここでは代表的な制御構文としての特殊形式/マクロを紹介しますが、Clojure標準ライブラリにはこの他にも多彩なマクロが含まれており、さらにはマクロを自ら定義することで独自の制御構文を追加することも簡単にできます。
;; TODO
let特殊形式
code:clojure
;; <束縛式> = <束縛フォーム> <初期値>
cf. letfn
do特殊形式
code:clojure
(do <式>*)
code:clojure
user=> (do (println "A") (println "B") (+ 1 2))
A
B
3
code:clojure
(defn foo []
(str "foo" "foo"))
「暗黙のdo」
if特殊形式
code:clojure
(if <条件式> <then式> <else式>?)
code:clojure
"18歳未満です"
"18歳以上です"
code:clojure
(if <条件式> <then式> nil)
cf. if-not, if-let, if-some
whenマクロ
code:clojure
(when <条件式> <then式>*)
code:clojure
(if <条件式> (do <then式>*))
cf. when-not, when-let, when-first, when-some
condマクロ
code:clojure
(cond <節>*)
;; <節> = <条件式> <then式>
cf. condp, cond->, cond->>
練習問題1
loop特殊形式
code:clojure
;; <束縛式> = <束縛フォーム> <初期値>
let によく似ている
recur 特殊形式と組み合わせて末尾再帰関数を簡潔に書くための構文
他言語におけるループ文などとは異なる
whileマクロ
code:clojure
(while <条件式> <本体式>*)
code:clojure
(loop []
(when <条件式>
<本体式>*
(recur)))
code:clojure
#_=> (println (str "i = " @i)) i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9
i = 10
nil
練習問題2
doseqマクロ
code:clojure
;; <ジェネレータ> = <シンボル> <式>
code:clojure
user=> (doseq [x (range 1 (inc 5))
#_=> (println "x = " x " y = " y)) x = 1 y = 1
x = 1 y = 2
x = 1 y = 3
x = 1 y = 4
x = 2 y = 1
x = 2 y = 2
x = 2 y = 3
x = 2 y = 4
x = 3 y = 1
x = 3 y = 2
x = 3 y = 3
x = 3 y = 4
x = 4 y = 1
x = 4 y = 2
x = 4 y = 3
x = 4 y = 4
x = 5 y = 1
x = 5 y = 2
x = 5 y = 3
x = 5 y = 4
nil
code:clojure
user=> (doseq [x (range 1 (inc 5))
#_=> (println "x = " x " y = " y)) x = 1 y = 2
x = 1 y = 3
x = 1 y = 4
x = 2 y = 1
x = 2 y = 3
x = 2 y = 4
x = 3 y = 1
x = 3 y = 2
x = 3 y = 4
x = 4 y = 1
x = 4 y = 2
x = 4 y = 3
x = 5 y = 1
x = 5 y = 2
x = 5 y = 3
x = 5 y = 4
nil
code:clojure
A
B
C
D
E
nil
cf. dotimes
forマクロ
code:clojure
;; <ジェネレータ> = <シンボル> <式>
code:clojure
("PreA" "PreB" "PreC" "PreD" "PreE")
練習問題3
caseマクロ
code:clojure
(case <式> <節>* <デフォルト値>?)
;; <節> = <定数> <式>
code:clojure
"Male"
code:clojure
"one"
code:clojure
user=> (case "abc"
#_=> "abc" (println "first") #_=> "def" (println "second")) first
nil
code:clojure
user=> (case "abc"
#_=> ("abc" "def") (do (println "first") #_=> (println "second"))) first
second
nil
clojure.core.match/matchマクロ
code:clojure
(clojure.core.match/match <対象式>
<節>*)
;; <節> = (<パターン> <ガード>?) <式>
;; <ガード> = :guard <述語関数>
code:project.clj
(defproject sandbox "0.1.0"
:main sandbox.hello-world
:profiles {:dev {:dependencies com.bhauman/rebel-readline "0.1.4"
code:clojure
user=> (require '[clojure.core.match :refer match]) nil
code:clojure
"Male"
"one"
user=> (match "abc"
#_=> "abc" (println "first") #_=> "def" (println "second")) first
nil
user=> (match "abc"
#_=> (:or "abc" "def") (do (println "first") #_=> (println "second"))) first
second
nil
code:clojure
#_=> (println (str "c = " c))) #_=> :else (println "nothing"))) b = B
c = C
nil
code:clojure
#_=> (println (str "c = " c))) #_=> :else (println "nothing"))) nothing
nil
code:clojure
#_=> ([("A" :as a :seq) x] :seq) (do (println a) #_=> :else (println "nothing"))) nil
code:clojure
#_=> (println (str "c = " c))) #_=> :else (println "nothing"))) b = B
c = C
nil
code:clojure
user=> (import java.util.Locale)
java.util.Locale
#_=> (v :guard #(instance? String %)) (println (.toUpperCase v Locale/ENGLISH)))) STRING LITERAL
nil
;; もしくは
user=> (let [obj "String Literal"
#_=> Integer (println "Integer!") #_=> String (println (.toUpperCase obj Locale/ENGLISH)))) STRING LITERAL
nil
練習問題4